package com.start.workflow.core.manage;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.start.common.details.BaseUserDetails;
import com.start.common.entity.StartBaseUser;
import com.start.common.exceptions.StartBusException;
import com.start.common.mybaitsplus.StartMBColumnTools;
import com.start.common.std.IStartUserKey;
import com.start.common.tools.StartEnumTools;
import com.start.common.tools.StartUserTools;
import com.start.common.user.StartUserInfoLoadService;
import com.start.security.StartTokenTools;
import com.start.workflow.act.StartActMyHandlerAuthTask;
import com.start.workflow.act.StartActWaitAuthTask;
import com.start.workflow.common.enums.StartActHandlerStatus;
import com.start.workflow.common.enums.StartActUserTaskConf;
import com.start.workflow.core.dispatcher.StartGlobalActInstEventDispatcher;
import com.start.workflow.core.dispatcher.StartGlobalActTaskEventDispatcher;
import com.start.workflow.core.load.StartActCustomCandidateLoad;
import com.start.workflow.core.mappers.StartActTaskMapper;
import com.start.workflow.core.service.StartActBusinessService;
import com.start.workflow.core.service.StartActModelConfigService;
import com.start.workflow.entity.StartActBusinessEntity;
import com.start.workflow.entity.StartActModelConfig;
import com.start.workflow.entity.StartActTaskEntity;
import com.start.workflow.vo.StartActNodeAction;
import com.start.workflow.vo.StartCustomActAuthUser;
import com.start.workflow.vo.StartSubmitActTask;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.*;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


@Component
public class StartActUserTaskManage {
    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;


    @Autowired
    private HistoryService historyService;
    @Autowired
    private StartActBusinessService businessService;

    @Autowired
    private StartActModelConfigService startActModelConfigService;


    @Autowired
    private StartActTaskMapper startActTaskMapper;


    /**
     * 流程节点添加审批人
     *
     * @param taskId        流程任务id
     * @param startUserType 用户体系类型
     * @param userId        用户id
     */
    public void addUserTaskCandidateUser(String taskId, String startUserType, String userId) {
        taskAddCandidateUser(taskId, StartUserTools.getStartUserKey(startUserType, userId), false);
    }

    public void taskAddCandidateUser(String taskId, String startUserKey, boolean isThrow) {
        if (taskService.createTaskQuery().taskId(taskId).count() < 0) {
            if (isThrow) {
                throw new StartBusException("该任务已被其他人审批无法添加审批候选人");
            }
            return;
        }
        long count = startActTaskMapper.countTaskCandidateUser(taskId, startUserKey, BpmnXMLConstants.ELEMENT_TASK_USER);
        if (count > 0) {
            return;
        }
        taskService.addCandidateUser(taskId, startUserKey);

    }

    /**
     * 添加候选人
     *
     * @param taskId
     * @param user
     */
    public void addUserTaskCandidateUser(String taskId, IStartUserKey user) {
        taskAddCandidateUser(taskId, user.getStartUserKey(), false);
    }

    /**
     * 处理节点审批
     *
     * @param taskId
     * @param actApply
     * @return
     */
    @Transactional
    public void handlerNodeApply(String taskId, StartSubmitActTask actApply) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new StartBusException("该任务已被审批");
        }
        BaseUserDetails currentUser = StartTokenTools.getCurrentUser();
        if (actApply.getAuthUser() == null) {
            StartCustomActAuthUser startCustomActAuthUser = new StartCustomActAuthUser();
            startCustomActAuthUser.setStartUserType(currentUser.getStartUserType());
            startCustomActAuthUser.setUserId(currentUser.getUserId());
            actApply.setAuthUser(startCustomActAuthUser);
        }
        if (!currentUser.isSuperUser() && !actApply.getAuthUser().getStartUserKey().equals(currentUser.getStartUserKey())) {
            throw new StartBusException("当前账号禁止代理审核");
        }
        List<IdentityLink> forTask = taskService.getIdentityLinksForTask(task.getId());
        if (forTask.stream().noneMatch(e -> e.getTaskId().equals(taskId) && e.getUserId().equals(actApply.getAuthUser().getStartUserKey()))) {
            throw new StartBusException("您不具备审核当前任务权限");
        }
        StartActHandlerStatus actHandlerStatus = StartEnumTools.findEnumByValue(StartActHandlerStatus.class, actApply.getHandlerStatus());
        if (actHandlerStatus == null) {
            throw new StartBusException("任务状态无效");
        } else if (actHandlerStatus == StartActHandlerStatus.agree) {
            userTaskAgree(task, actApply);
        } else if (actHandlerStatus == StartActHandlerStatus.REJECT) {
            userTaskReject(task, actApply);
        } else if (actHandlerStatus == StartActHandlerStatus.GO_BACK) {
            userTaskGoBack(task, actApply);
        } else {
            throw new StartBusException("任务状态无效");
        }
    }

    /**
     * 任务退回
     *
     * @param task
     * @param actApply
     */
    public void userTaskGoBack(Task task, StartSubmitActTask actApply) {
        String targetTaskDefKey = actApply.getTargetTaskDefKey();
        if (StringUtils.isAllBlank(targetTaskDefKey)) {
            throw new StartBusException("未指定驳回到任务定义key");
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        FlowElement targetElement = bpmnModel.getMainProcess().getFlowElement(targetTaskDefKey);
        if (targetElement == null) {
            throw new StartBusException("未找到目标节点任务：" + targetTaskDefKey);
        }
        if (!(targetElement instanceof UserTask)) {
            throw new StartBusException("目标节点不是有一个有效的userTask:" + targetTaskDefKey);
        }

        FlowNode curNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
        FlowNode targetNode = (FlowNode) targetElement;
        String oldTaskHandlerUserKey = null;
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceEndTime().desc().list();
        for (HistoricTaskInstance hti : list) {
            if (!Objects.equals(hti.getTaskDefinitionKey(), task.getTaskDefinitionKey())) {
                historyService.deleteHistoricTaskInstance(hti.getId());
                continue;
            }
            historyService.deleteHistoricTaskInstance(hti.getId());
            oldTaskHandlerUserKey = hti.getAssignee();
            break;
        }
        List<SequenceFlow> srcOutgoingFlows = new ArrayList<>(curNode.getOutgoingFlows());
        List<SequenceFlow> newOutgoingFlows = new ArrayList<>();
        SequenceFlow sequenceFlow = new SequenceFlow();
        sequenceFlow.setId("start_temp_sequence");
        sequenceFlow.setSourceFlowElement(curNode);
        sequenceFlow.setTargetFlowElement(targetNode);
        newOutgoingFlows.add(sequenceFlow);
        curNode.setOutgoingFlows(newOutgoingFlows);
        taskService.complete(task.getId());
        curNode.setOutgoingFlows(srcOutgoingFlows);
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        if (actApply.getTargetTaskAuthUsers() == null) {
            actApply.setTargetTaskAuthUsers(new ArrayList<>());
        }
        List<StartCustomActAuthUser> targetTaskAuthUsers = actApply.getTargetTaskAuthUsers();
        if (StringUtils.isNotBlank(oldTaskHandlerUserKey)) {
            String finalOldTaskHandlerUserKey = oldTaskHandlerUserKey;
            if (targetTaskAuthUsers.stream().noneMatch(e -> e.getStartUserKey().equals(finalOldTaskHandlerUserKey))) {
                StartCustomActAuthUser startCustomActAuthUser = new StartCustomActAuthUser(oldTaskHandlerUserKey);
                targetTaskAuthUsers.add(startCustomActAuthUser);
            }

        }
        if (targetTaskAuthUsers == null || targetTaskAuthUsers.isEmpty()) return;
        for (Task t : tasks) {
            if (!t.getTaskDefinitionKey().equals(task.getTaskDefinitionKey())) {
                continue;
            }
            for (StartCustomActAuthUser authUser : targetTaskAuthUsers) {
                addUserTaskCandidateUser(t.getId(), authUser);
            }
        }


    }

    /**
     * 同意任务
     *
     * @param task
     * @param actApply
     */
    private void userTaskAgree(Task task, StartSubmitActTask actApply) {
        BaseUserDetails currentUser = StartTokenTools.getCurrentUser();
        StartActBusinessEntity actBusinessEntity = businessService.getOneByProcessInstanceId(task.getProcessInstanceId());
        checkUserHasAuthTask(task, currentUser);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        StartActModelConfig config = startActModelConfigService.getActModelConfig(processInstance.getDeploymentId(), task.getTaskDefinitionKey(), StartActUserTaskConf.FORM_TABLE);
        if (config != null) {
            if (actApply.getNodeFormData() == null || actApply.getNodeFormData().isEmpty()) {
                throw new StartBusException("该任务需要您提交相关数据");
            }
            StartActHandlerStatus actHandlerStatus = StartEnumTools.findEnumByValue(StartActHandlerStatus.class, actApply.getHandlerStatus());
            StartActFormManager.getInstance().delProcTaskDataByDefKey(config, task.getProcessInstanceId(), task.getTaskDefinitionKey());
            StartActFormManager.getInstance().saveProcTaskData(config, task.getProcessInstanceId(), task.getId(), actHandlerStatus, actApply.getNodeFormData());
        }
        StartGlobalActTaskEventDispatcher.getInstance().onTaskCompleteBefore(actBusinessEntity, task);
        userTaskComplete(task.getProcessInstanceId(), task.getId(), actApply);
        handlerNextUserTaskAuthUser(actBusinessEntity, actApply.getNextTaskAuthUser());
        StartGlobalActTaskEventDispatcher.getInstance().onTaskCompleteAfter(actBusinessEntity, task);
    }

    private void handlerNextUserTaskAuthUser(StartActBusinessEntity processInstance, List<StartCustomActAuthUser> authUsers) {
        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstance.getProcInstId()).active();
        if (taskQuery.count() < 1) {
            return;
        }
        if (authUsers == null || authUsers.isEmpty()) {
            return;
        }
        List<Task> list = taskQuery.list();
        if (list.isEmpty()) {
            return;
        }
        for (Task task : list) {

            StartActModelConfig config = startActModelConfigService.getActModelConfig(processInstance.getProcDeployId(), task.getTaskDefinitionKey(), StartActUserTaskConf.CANDIDATE_USER);
            if (config == null) {
                continue;
            }
            if (!StartActCustomCandidateLoad.CUSTOM_AUTH_CANDIDATE.equals(config.getConfKey())) {
                continue;
            }
            for (StartCustomActAuthUser user : authUsers) {
                addUserTaskCandidateUser(task.getId(), user.getStartUserType(), user.getUserId());
            }

        }
    }


    /**
     * 完成任务
     *
     * @param actInstId
     * @param taskId
     * @param actNodeAction
     */
    private void userTaskComplete(String actInstId, String taskId, StartSubmitActTask actNodeAction) {
        taskService.setAssignee(taskId, actNodeAction.getAuthUser().getStartUserKey());
        StartActNodeAction nodeAction = actNodeAction.userAction();
        taskService.addComment(taskId, actInstId, BpmnXMLConstants.ELEMENT_TASK_USER, nodeAction.toJSONString());
        taskService.complete(taskId);
    }


    /**
     * 检查用户是否可审批当前任务
     *
     * @param task        流程任务
     * @param currentUser 当前用户
     */
    private void checkUserHasAuthTask(Task task, BaseUserDetails currentUser) {
        List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(task.getId());
        if (currentUser.isSuperUser()) {
            return;
        }
        if (identityLinksForTask.isEmpty()) {
            throw new StartBusException("当前任务未设置候选人,请联系管理员");
        }
        if (identityLinksForTask.stream().noneMatch(e -> e.getUserId().equals(currentUser.getStartUserKey()))) {
            throw new StartBusException("您不是当前任务的审批候选人,无权审批当前任务");
        }
    }

    /**
     * 驳回
     *
     * @param task
     * @param actApply
     */
    private void userTaskReject(Task task, StartSubmitActTask actApply) {
        BaseUserDetails currentUser = StartTokenTools.getCurrentUser();
        if (StringUtils.isAllBlank(actApply.getHandlerOpinion())) {
            throw new StartBusException("请输入驳回原因");
        }
        StartActBusinessEntity startActBusinessEntity = businessService.getOneByProcessInstanceId(task.getProcessInstanceId());
        checkUserHasAuthTask(task, currentUser);
        taskService.setAssignee(task.getId(), currentUser.getStartUserKey());
        if (StringUtils.isNotBlank(actApply.getTargetTaskDefKey())) {
            throw new StartBusException("暂不支持任务驳回到任意节点");
        }
        StartGlobalActTaskEventDispatcher.getInstance().onTaskRejectBefore(startActBusinessEntity, task);
        startActBusinessEntity.setProcHandlerState(actApply.getHandlerStatus());
        userTaskComplete(task.getProcessInstanceId(), task.getId(), actApply);
        runtimeService.deleteProcessInstance(task.getProcessInstanceId(), actApply.userAction().toJSONString());
        businessService.updateById(startActBusinessEntity);
        StartGlobalActTaskEventDispatcher.getInstance().onTaskRejectAfter(startActBusinessEntity, task);
        StartGlobalActInstEventDispatcher.getInstance().onActInstanceRejectAfter(startActBusinessEntity);
    }

    /**
     * 分页查询正则执行的流程任务(管理端)
     *
     * @param page
     * @param queryWrapper
     * @return
     */
    public Page<StartActTaskEntity> pageActTask(Page<StartActTaskEntity> page, QueryWrapper<StartActTaskEntity> queryWrapper) {
        page = startActTaskMapper.pageActTask(page, queryWrapper);
        List<StartActTaskEntity> records = page.getRecords();
        if (records.isEmpty()) {
            return page;
        }
        Map<String, StartBaseUser> userMap = new HashMap<>();
        for (StartActTaskEntity task : records) {
            StartBaseUser startBaseUser = mapTaskFindUser(userMap, task.getProcStartUserType(), task.getProcStartUserId());
            task.setActStartUser(startBaseUser);
        }
        return page;
    }

    /**
     * 查询我的待处理任务
     *
     * @param page
     * @param queryWrapper
     * @return
     */
    public IPage<StartActWaitAuthTask> pageMyWaitAuthTask(IPage<StartActWaitAuthTask> page, QueryWrapper<StartActWaitAuthTask> queryWrapper) {
        String currentUserKey = StartTokenTools.getCurrentUserKey();
        queryWrapper.eq(StartMBColumnTools.getColumn(StartActWaitAuthTask::getTaskCandidateUserKey), currentUserKey);
        Page<StartActWaitAuthTask> authTaskPage = startActTaskMapper.pageMyWaitAuthTask(page, queryWrapper);
        List<StartActWaitAuthTask> records = authTaskPage.getRecords();
        if (records.isEmpty()) {
            return authTaskPage;
        }
        Map<String, StartBaseUser> userMap = new HashMap<>();
        for (StartActWaitAuthTask task : records) {
            StartBaseUser startBaseUser = mapTaskFindUser(userMap, task.getProcStartUserType(), task.getProcStartUserId());
            task.setActStartUser(startBaseUser);
        }
        return authTaskPage;
    }

    /**
     * map 查找用户
     *
     * @param map
     * @param startUserType
     * @param userId
     * @return
     */
    private StartBaseUser mapTaskFindUser(Map<String, StartBaseUser> map, String startUserType, String userId) {
        return map.computeIfAbsent(startUserType + userId, k -> {
            StartBaseUser baseUser = StartUserInfoLoadService.getInstance().getUserById(startUserType, userId);
            if (baseUser == null) {
                throw new StartBusException("为找到startUserKey为：" + k + "的用户");
            }
            return baseUser;
        });
    }

    /**
     * 查询我的已办
     *
     * @param page
     * @param queryWrapper
     * @return
     */
    public IPage<StartActMyHandlerAuthTask> pageMyDoneTask(IPage<StartActMyHandlerAuthTask> page, QueryWrapper<StartActMyHandlerAuthTask> queryWrapper) {
        String currentUserKey = StartTokenTools.getCurrentUserKey();
        queryWrapper.eq(StartMBColumnTools.getColumn(StartActWaitAuthTask::getProcStartUserKey), currentUserKey);
        Page<StartActMyHandlerAuthTask> authTaskPage = startActTaskMapper.pageMyDoneTask(page, queryWrapper);
        List<StartActMyHandlerAuthTask> records = authTaskPage.getRecords();
        if (records.isEmpty()) {
            return authTaskPage;
        }
        Map<String, StartBaseUser> userMap = new HashMap<>();
        for (StartActMyHandlerAuthTask task : records) {
            StartBaseUser startBaseUser = mapTaskFindUser(userMap, task.getProcStartUserType(), task.getProcStartUserId());
            task.setActStartUser(startBaseUser);
        }
        return authTaskPage;
    }

    /**
     * 删除任务候选处理人
     *
     * @param taskId
     * @param startCustomActAuthUsers
     */
    public void delTaskCandidateUser(String taskId, List<StartCustomActAuthUser> startCustomActAuthUsers) {
        if (startCustomActAuthUsers == null) {
            return;
        }
        if (startCustomActAuthUsers.isEmpty()) return;
        if (taskService.createTaskQuery().taskId(taskId).count() < 1) {
            throw new StartBusException("该任务已被其他人处理");
        }
        List<String> startUserKeys = startCustomActAuthUsers.stream().map(StartCustomActAuthUser::getStartUserKey).filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList());
        if (startUserKeys.isEmpty()) return;
        for (String startUserKey : startUserKeys) {
            taskService.deleteCandidateUser(taskId, startUserKey);
        }
    }

    /**
     * 查询全部任务候选处理人
     *
     * @param taskId
     * @return
     */
    public List<StartBaseUser> listTaskCandidateUser(String taskId) {
        if (taskService.createTaskQuery().taskId(taskId).count() < 1) {
            throw new StartBusException("当前任务已被其他人处理");
        }
        List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(taskId);
        List<String> startUserKeys = identityLinksForTask.stream()
                .filter(e -> "candidate".equals(e.getType()) && StringUtils.isNotBlank(e.getUserId()))
                .map(IdentityLink::getUserId)
                .distinct()
                .collect(Collectors.toList());
        if (startUserKeys.isEmpty()) return new ArrayList<>();
        List<StartBaseUser> users = StartUserInfoLoadService.getInstance().getUsers(startUserKeys);
        for (StartBaseUser user : users) {
            user.setUserPwd(null);
        }
        return users;

    }
}
